import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;
import java.util.Vector;


public class Device {
	private Vector<String> aliases = new Vector<String>();
	private Vector<String> commands = new Vector<String>();
	// The access control list for this device. Only these email
	// addresses may send messages to this device.
	// If null, then anyone can send commands (bad security!)
	private Vector<String> allowedUsers = null;
	private String executionCommand = null;
	private String outputFilePath = null;
	
	public void addAlias(String alias) {
		this.aliases.add(alias);
	}
	
	public Vector<String> getAliases() {
		return this.aliases;
	}
	
	public void setExecutablePath(String command) {
		this.executionCommand = command;
	}
	
	public void setOutputFilePath(String path) {
		this.outputFilePath = path;
	}
	
	public String getOutputFilePath() {
		return this.outputFilePath;
	}
	
	public void addCommand(String command) {
		this.commands.add(command);
	}

	public void setAllowedUsers(Vector<String> newUserList) {
		this.allowedUsers = newUserList;
	}
	
	public Vector<String> getAllowedUsers() {
		return this.allowedUsers;
	}
	
	public boolean isSenderValid(String sender) {
		// No authentication used. User should be warned of this case.
		if (this.allowedUsers == null) return true;
		
		int numUsers = this.allowedUsers.size();
		for (int i = 0; i < numUsers; ++i) {
			if (sender.compareToIgnoreCase(this.allowedUsers.get(i)) == 0) {
				return true;
			}
		}
		
		// The sender didn't match any allowed users, so access should be denied.
		return false;
	}
	
	// TODO: Need to consider this.
	// Will it be more efficient to:
	// 1. Have a poller contain a list of Devices and name checking is a
	//    linear search for each device. Device addition and removal is
	//    a snap. Fastest to implement.
	// 2. The poller maps aliases to device instances and name checking is
	//    a simple dictionary lookup. However, device addition and removal
	//    is slightly more complicated (but code-able in like 15 minutes)
	//    and we'll also have to resolve collisions somehow.
	public boolean isStringAlias (String name) {
		int numAliases = this.aliases.size();
		for (int i = 0; i < numAliases; ++i) {
			if (name.compareToIgnoreCase(this.aliases.elementAt(i)) == 0) {
				return true;
			}
		}
		return false;
	}
	
	public boolean handleCommand(String[] tokens) {
		int numCommands = this.commands.size();
		for (int i = 0; i < numCommands; ++i) {
			//
			String curCommandDescriptor = this.commands.elementAt(i);
			int commandIndex = curCommandDescriptor.indexOf(',');
			String curCommand = curCommandDescriptor.substring(0, commandIndex);
			curCommand.trim();
			String curArgs = curCommandDescriptor.substring(commandIndex + 2);
			curArgs.trim();
			String[] commandTokens = curCommand.split("[ \t]+");
			 
			// If the commands have different lengths then we shouldn't even consider it.
			if (commandTokens.length != tokens.length - 1) continue;
			
			boolean commandMatches = true;
			
			for (int j = 0; j < commandTokens.length; ++j) {
				if (!commandTokens[j].equalsIgnoreCase(tokens[j + 1])) {
					commandMatches = false;
					break;
				}
			}
			 
			if (commandMatches) {
				System.out.println("Executing command: " + commandTokens[0]);
				this.executeCommand(curArgs);
				return true;
			}
		}
		
		
		// In this case, the tokens do not match any possible command for
		// this device.
		return false;
	}
	
	private void executeCommand(String args) {
		if (this.executionCommand != null) {
			try {
				//GmailUtils.pr(message);
				String command = this.executionCommand + " " + args;
				GmailUtils.pr("Executing: " + command);
    			Runtime rt = Runtime.getRuntime();
    			rt.exec(command, null);
			} catch (Exception e) {
				GmailUtils.pr("Error: Could not execute command!");
			}
		} else {
			this.writeToOutputFile(args);
		}
	}
	
	private void writeToOutputFile(String message) {
		try {
			File file = new File(this.outputFilePath);
			if (!file.exists()) {
				file.createNewFile();
			}
			FileOutputStream outputStream = new FileOutputStream(this.outputFilePath, false);
			outputStream.write(message.getBytes());
			outputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public Vector<String> getCommandList() {
		return this.commands;
	}
}
